home *** CD-ROM | disk | FTP | other *** search
- From: mjy@sdti.UUCP (Michael J. Young)
- Newsgroups: comp.sources.misc
- Subject: v02i005: File System Analyzer Tool
- Message-ID: <7079@ncoast.UUCP>
- Date: 15 Jan 88 00:31:36 GMT
- Approved: allbery@ncoast.UUCP
-
- Comp.Sources.Misc: Volume 2, Issue 5
- Submitted-By: Michael J. Young <mjy@sdti.UUCP>
- Archive-Name: sys5-fs-analysis
-
- I've tried posting this to comp.sources.unix a number of times (since
- October!), but I seem to have discovered a black hole between here and that
- newsgroup. Since I'm starting to get gentle reminders that I'd promised
- this awhile ago, I decided to punt and try posting here.
-
- This is a tool that I wrote to determine file system fragmentation on my
- Microport box. I mentioned it a few months ago in comp.unix.wizards, and
- got a lot of requests for the source. It's not very elegant, and it's
- specific to System V, but it's functional.
-
- I'd really like to see it ported to other systems (I only have access to
- System V). If anyone has lots of spare time(!) and wants to port it, there
- are some notes in the README file for things to watch out for. Please send
- me your changes so I can incorporate them into my version. If you are
- really bored, feel free to add an in-place disk-compression capability! :-)
-
- --------------------- cut here -------- cut here --------------------------
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then unpack
- # it by saving it into a file and typing "sh file". To overwrite existing
- # files, type "sh file -c". You can also feed this as standard input via
- # unshar, or by typing "sh <file", e.g.. If this archive is complete, you
- # will see the following message at the end:
- # "End of archive 1 (of 1)."
- # Contents: README fsanalyze.8 fsanalyze.c
- # Wrapped by mjy@sdti on Thu Jan 7 15:46:01 1988
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- if test -f README -a "${1}" != "-c" ; then
- echo shar: Will not over-write existing file \"README\"
- else
- echo shar: Extracting \"README\" \(7915 characters\)
- sed "s/^X//" >README <<'END_OF_README'
- XFSANALYZE - System V File System Analyzer tool - v2.04, 7 Januaray 1988
- X
- XAuthor : Michael J. Young
- XUSmail : Software Development Technologies, Inc.
- X 375 Dutton Rd
- X Sudbury MA 01776
- XUUCP : {decvax|harvard|linus|mit-eddie}!necntc!necis!mrst!sdti!mjy
- XInternet : mjy%sdti.uucp@harvard.harvard.edu
- X
- X =========================================================================
- X Note : This program has been placed in the public domain to permit
- X unrestricted distribution and use. I have placed no copyright on it, but
- X I request that you keep me informed about any enhancements and bug fixes
- X you make so I can keep an up-to-date copy for further distribution.
- X =========================================================================
- X
- X Fsanalyze is a simple tool that estimates file system fragmentation. It
- X accomplishes this by scanning the data blocks for each i-node in the
- X file system, looking for block numbers that are out of sequence. In
- X effect, it is counting the number of disk seeks required to read the
- X entire file in sequence. Fragmentation is then computed as the ratio
- X of actual "seeks" to the potential number of "seeks" if the file were
- X completely fragmented.
- X
- X Fsanalyze also provides statistics regarding the number (and identity) of
- X files that are very large, and excessively large directories.
- X
- X Excessively large directories are directories that are over 5120 bytes
- X long (320 entries). Directories larger than this require data block
- X indirection, making file searches very inefficient.
- X
- X After the general file system statistics are displayed, fsanalyze lists
- X the 10 most fragmented i-nodes in the file system.
- X The 10 most fragmented files are listed in decreasing order of
- X fragmentation based on the absolute number of fragments. For example,
- X a 100-block file that contains 40 individual fragments is 39.39%
- X fragmented (39 seeks / 99 potential seeks), but is listed before
- X a 2-block file that contains 2 fragments (100% fragmented). Thus, larger
- X fragmented files (which have a greater effect on file system performance)
- X are listed before small files.
- X
- XRevision History:
- X
- X Version 2.0 is a major rewrite of fsanalyze that adds the following new
- X features:
- X - The ability to analyze individual files
- X - Display of the 10 most fragmented files in a file system
- X - Enhanced error checking on the file system argument
- X
- X Version 2.01 contains a minor modification in which fsanalyze executes
- X /etc/fsstat to determine the health of the file system before analyzing
- X it.
- X
- X Version 2.02 contains a minor modification to print out a warning message
- X if the file system being analyzed is currently mounted.
- X
- X Version 2.03 contains a fix for a minor bug in which the size of the
- X volume data block size would be printed out incorrectly for large
- X file systems.
- X
- X Version 2.04 incorporates the library function l3tol to access inode
- X block numbers.
- X
- XInstallation:
- X
- X This program is so simple that I didn't bother to create a Makefile. To
- X build fsanalyze the steps are:
- X cc -O -o fsanalyze fsanalyze.c
- X mv fsanalyze /bin
- X Where fsanalyze is installed doesn't really matter, but I would recommend
- X placing it somewhere in the root filesystem, which is always mounted. I
- X typically run fsanalyze during my backup procedure, while my other
- X filesystems are unmounted.
- X
- XUsage:
- X fsanalyze [-flags] special [file [...]]
- X
- X If the optional filename arguments are missing, the entire file system
- X is analyzed. If present, the specified files are analyzed and reported
- X individually.
- X
- X [flags] include the following:
- X d display i-node numbers as they are examined
- X e report file size inconsistencies - the inode numbers are reported
- X for files where the file size and number of data blocks are
- X inconsistent.
- X i report double and triple indirection - the inode numbers are
- X reported for files that contain double and/or triple indirection.
- X o overrides error checking on file system. Use this flag if the
- X file system you are analyzing is damaged. Note that fsanalyze
- X may give erroneous results if used on a damaged file system, but
- X the file system itself will not be affected.
- X
- X Example:
- X fsanalyze /dev/dsk/0s2 /* analyzes an entire file system */
- X fsanalyze /dev/dsk/0s2 * /* analyzes all files in the current
- X * directory of the file system */
- X
- X Since fsanalyze uses the superblock info ON THE DISK, more accurate
- X results will be returned fsanalyze is run on an unmounted, or read-only
- X mounted file system immediately after a sync(2).
- X
- X Note that fsanalyze will work with 512- or 1024-byte filesystems. In
- X filesystems with 1024-byte blocking-factors, free-blocks, etc., are
- X reported as 1024-byte blocks (unlike df(1) and du(1)). Therefore, the total
- X number of free blocks in your file system will be reported as half the
- X value reported by df(1). I prefer it this way. If you want it the
- X other way, it requires a simple change to print_report().
- X
- X Since fsanalyze does its work the old fashioned way (brute-force), it
- X must scan through the file system inode by inode. It therefore takes
- X about as long as fsck to do its work. Be patient.
- X
- XBugs:
- X Please report any bugs (and possible fixes) to me, so I can keep my
- X source up-to-date. I would also like to make fsanalyze work on other
- X unix systems, but I don't have access to anything but my little
- X uport. If you can help me port it to BSD or anything else, I would
- X appreciate it.
- X
- XPorting fsanalyze to other systems:
- X Fsanalyze takes its information on the structure of the file system
- X from /usr/include/sys/filsys.h and /usr/include/sys/ino.h. As long
- X as these files (or equivalent) are present on your system, porting
- X should be straightforward. When looking for the equivalent of these
- X files on your system, be sure to get the disk-based versions, rather
- X than the in-memory structures -- they are different. In System V
- X derivatives, these structures should be documented as fs(4 or 5) and
- X inode(4 or 5), on BSD systems and V7 systems as filsys(5), and on
- X XENIX 3.0 and 5.0 as filesystem(F).
- X
- X Fsanalyze uses fsstat(1M) to determine file system integrity. I don't
- X think this program is available in other versions of Unix. If your
- X system doesn't have it (or anything like it), you will have to rewrite
- X check_fs(). The kinds of things to check when testing for file system
- X integrity include:
- X 1. Make sure the device is block structured.
- X 2. Determine whether or not the device is mounted.
- X 3. Check s_state in the filsys structure to see if it is in a
- X known state.
- X 4. Anying else you can think of.
- X I used fsstat because it seems to be more robust than anything I
- X could come up with. It also appears to use undocumented information
- X to determine file system status.
- X
- X fsstat(1M) returns 0 if the file system is ok, 1 if it needs to be
- X checked, 2 if it is mounted, and 3 for other errors. Unfortunately,
- X the exit status appears to be byte-swapped when fsanalyze gets it.
- X I'm not sure if this is a bug in system(3S), or my use of it, but
- X be careful when porting to your system.
- X
- X I attempted to use types defined in /usr/include/sys/types.h wherever
- X possible to ensure portability. I don't think I made any assumptions
- X about sizeof(int) == sizeof(long) == sizeof(char *), so there should
- X be no problems there.
- X
- X Please try to make changes to the source using #defines and #ifdefs
- X wherever possible, and please email me the changes you make along with
- X a description of the system you ported it to (and problems encountered),
- X so I can merge all the changes into a single copy.
- X
- X Thanks and good luck!
- END_OF_README
- if test 7915 -ne `wc -c <README`; then
- echo shar: \"README\" unpacked with wrong size!
- fi
- # end of overwriting check
- fi
- if test -f fsanalyze.8 -a "${1}" != "-c" ; then
- echo shar: Will not over-write existing file \"fsanalyze.8\"
- else
- echo shar: Extracting \"fsanalyze.8\" \(3441 characters\)
- sed "s/^X//" >fsanalyze.8 <<'END_OF_fsanalyze.8'
- X.TH FSANALYZE 8
- X.ad b
- X.SH NAME
- Xfsanalyze \- a file system analyzer tool
- X.SH SYNOPSIS
- X.B fsanalyze
- X[
- X.B \-deio
- X]
- X.B special
- X[
- X.B files
- X]
- X.SH DESCRIPTION
- X.I Fsanalyze
- Xis a simple tool that estimates file or file system fragmentation. It accomplishes
- Xthis by scanning the data blocks of each inode looking for block numbers that
- Xare out of sequence. In effect, it is counting the number of disk seeks
- Xrequired to read a file sequentially. Fragmentation is then computed as the
- Xratio of actual "seeks" to the potential number "seeks" which would be
- Xrequired if the file were completely fragmented.
- X.LP
- XIf the optional \fBfiles\fR argument is omitted,
- X.I fsanalyze
- Xwill analyze the entire file system specified by the \fBspecial\fR argument.
- X\fBSpecial\fR must be a block-oriented file system device.
- X.I Fsanalyze
- Xreports various statistics regarding file system usage and data-block
- Xindirection, plus a list of the 10 most fragmented inodes in the file system.
- XThe 10 most fragmented files are listed in decreasing order of fragmentation
- Xbased on the absolute number of fragments. For example a 100-block file
- Xthat contains 40 individual fragments is 39.39% fragmented (39 seeks / 99
- Xpotential seeks), but is listed before a 2-block file that contains 2
- Xfragments (100% fragmented).
- X.LP
- XIf the \fBfiles\fR argument is present,
- X.I fsanalyze
- Xwill report the fragmentation of the designated files only.
- X.LP
- XBefore performing any analysis,
- X.I fsanalyze
- Xchecks the file system integrity by invoking
- X.I fsstat (1M).
- XIf the file system needs to be checked,
- X.I fsanalyze
- Xterminates with a message to that effect. If a non-root file system is
- Xmounted, a warning message is displayed, but analysis continues. When
- Xused on a mounted file system, it is recommended that a
- X.I sync (1)
- Xbe performed immediately prior to running
- X.I fsanalyze .
- XIf
- X.I sync (1)
- Xis not performed, the file system will not be damaged, but erroneous
- Xstatistics may be reported.
- X.SH OPTIONS
- X.IP \fB\-d\fR
- Xdisplay inode numbers as they are examined. This flag makes it easy to
- Xchart the progress of fsanalyze through the file system. Used mainly for
- Xdebugging.
- X.IP \fB\-e\fR
- Xreport file size inconsistencies - the inode numbers are reported for files
- Xwhere the file size and number of data blocks are inconsistent. This
- Xoption provides the same information as
- X.I fsck (1)
- Xphase 1.
- X.IP \fB\-i\fR
- Xreport double and triple indirection - the inode numbers are reported for
- Xfiles that contain double and/or triple data-block indirection.
- X.IP \fB\-o\fR
- Xoverrides file system integrity checks. The file system will be analyzed
- Xeven if
- X.I fsstat (1M)
- Xreports that it is damaged. Note that
- X.I fsanalyze
- Xmay give erroneous results if used on a damaged file system, but the file
- Xsystem itself will not be affected.
- X.SH EXAMPLES
- Xfsanalyze /dev/dsk/0s2 # analyze file system
- X.br
- Xfsanalyze /dev/dsk/0s2 * # analyze files in current dir
- X.SH FILES
- X/usr/include/sys/filesys.h super block structure
- X.br
- X/usr/include/sys/ino.h disk inode structure
- X.br
- X/usr/include/sys/param.h system parameters
- X.br
- X/usr/include/sys/types.h system type definitions
- X.SH BUGS
- X.I Fsanalyze
- Xis supported by System V Release 2. It has not yet been ported to any
- Xother version of Unix.
- X.SH "SEE ALSO"
- Xfsstat(1M), fsck(1M), fsdb(1M)
- X.SH AUTHOR
- XMichael J. Young
- X.br
- X{decvax|harvard|linus|mit-eddie}!necntc!necis!mrst!sdti!mjy
- X.br
- XInternet : mjy%sdti.uucp@harvard.harvard.edu
- X.SH VERSION
- Xv2.04 \- 7 January 1988
- END_OF_fsanalyze.8
- if test 3441 -ne `wc -c <fsanalyze.8`; then
- echo shar: \"fsanalyze.8\" unpacked with wrong size!
- fi
- # end of overwriting check
- fi
- if test -f fsanalyze.c -a "${1}" != "-c" ; then
- echo shar: Will not over-write existing file \"fsanalyze.c\"
- else
- echo shar: Extracting \"fsanalyze.c\" \(25225 characters\)
- sed "s/^X//" >fsanalyze.c <<'END_OF_fsanalyze.c'
- X/*
- X * fsanalyze.c - file system analyzer - v2.04; 7 January 1988
- X *
- X * Author : Michael J. Young
- X * USmail : Software Development Technologies, Inc.
- X * 375 Dutton Rd
- X * Sudbury MA 01776
- X * UUCP : {decvax|harvard|linus|mit-eddie}!necntc!necis!mrst!sdti!mjy
- X * Internet : mjy%sdti.uucp@harvard.harvard.edu
- X *
- X * =========================================================================
- X * Note : This program has been placed in the public domain to permit
- X * unrestricted distribution and use. I have placed no copyright on it, but
- X * I request that you keep me informed about any enhancements and bug fixes
- X * you make so I can keep an up-to-date copy for further distribution.
- X * =========================================================================
- X *
- X * fsanalyze is a simple tool that estimates file system fragmentation. It
- X * accomplishes this by scanning the data blocks for each i-node in the
- X * file system, looking for block numbers that are out of sequence.
- X * Fragmentation is then computed as the ratio of out-of-sequence blocks to
- X * total data blocks.
- X *
- X * fsanalyze also provides statistics regarding the number (and identity) of
- X * files that are very large, and especially excessively large directories
- X * (which are very inefficient).
- X *
- X * To build fsanalyze the steps are:
- X * cc -O -o fsanalyze fsanalyze.c
- X * mv fsanalyze /bin
- X *
- X * Usage:
- X * fsanalyze [-flags] special [file] ...
- X *
- X * where [flags] include the following:
- X * d display i-node numbers as they are examined
- X * e report file size inconsistencies
- X * i report inodes numbers with double and triple indirection
- X * o override error checking on file system superblock
- X *
- X * If the optional file argument(s) are present, only the specified
- X * individual files on the file system are analyzed. If absent, the
- X * entire file system is analyzed.
- X *
- X * Example:
- X * fsanalyze /dev/dsk/0s2
- X *
- X * Since fsanalyze uses the superblock info ON THE DISK, more accurate
- X * results will be returned if sync(1) is executed immediately prior to
- X * fsanalyze.
- X */
- X
- X/*
- X * Modification History:
- X *
- X * Date Author Description
- X * ----------- -------- -----------------------------------------------
- X * 28 Jul 1987 MJY Originated
- X * 5 Oct 1987 MJY Capability to analyze individual files,
- X * Added error checking to file system argument,
- X * Added -o flag
- X * Prints out volume and file system name in summary
- X * 12 Oct 1987 MJY Use /etc/fsstat to do file system validity
- X * checking
- X * 9 Nov 1987 MJY print out warning if file system is mounted
- X * 12 Nov 1987 MJY Volume size statistics now long instead of int
- X * 7 Jan 1988 MJY Modified blk_no() to use l3tol()
- X */
- X
- X
- X# include <stdio.h>
- X# include <sys/types.h>
- X# include <sys/ino.h>
- X# include <sys/param.h>
- X# include <sys/filsys.h>
- X# include <sys/stat.h>
- X
- X# define BLK_SIZE 512 /* block size */
- X# define IBLK 2 /* block number of first i-node */
- X
- X# define I_BLOCK_FACTOR 32 /* number of i-nodes per block */
- X
- X/*
- X * file mode definitions
- X */
- X# define FILE_TYPE(a) ((a)->di_mode & S_IFMT)
- X# define IS_SPECIAL(a) (((a) & S_IFMT == S_IFBLK) || ((a) & S_IFMT == S_IFCHR) || ((a) & S_IFMT == S_IFIFO))
- X
- X/*
- X * basic definitions
- X */
- X# define FALSE 0
- X# define TRUE 1
- X
- Xtypedef int boolean;
- X
- X/*
- X * per-file statistics structure
- X */
- Xstruct file_data {
- X int inode; /* i-node number */
- X long total_blocks; /* total blocks in file (incl
- X * indirect blocks */
- X long data_blocks; /* total data blocks in file */
- X long potential_seeks; /* number of potential seeks in file */
- X long seeks; /* actual seeks in file */
- X float fragm; /* fragmentation (seeks/pot.seeks) */
- X };
- X
- XFILE *fsys = NULL; /* file system under test */
- Xchar *special = NULL; /* file system device name */
- X
- X/*
- X * file system static data
- X */
- Xstruct filsys super = {0}; /* holds file system super block */
- Xint fs_type = 1; /* file system logical block size
- X * (in 512 byte units) */
- Xlong num_inodes = 0; /* number of i-nodes in file system */
- X
- X/*
- X * calculated global statistics
- X */
- Xlong blocks = 0; /* running block count */
- Xlong free_inodes = 0; /* number of unused i-nodes */
- Xlong potential_seeks = 0; /* potential number of disk seeks
- X * during sequential access of a
- X * file */
- Xlong seeks = 0; /* actual number of disk seeks
- X * required during sequential access
- X * of a file */
- Xlong indirects = 0; /* number of files w/ more than
- X * 10 data blocks */
- Xlong double_indirects = 0; /* number of files w/ more than
- X /* one level of indirection */
- Xlong triple_indirects = 0; /* number of files w/ more than
- X * two levels of indirection */
- Xint big_directories = 0; /* number of directories with
- X * indirection */
- Xint num_directories = 0; /* number of directories */
- Xint linked_files = 0; /* number of multiply-linked files */
- Xint size_errors = 0; /* number of file size discrepancies */
- X
- X
- Xstruct file_data file_log[10] = {0}; /* 10 worst offenders */
- X
- X
- X/*
- X * fsanalyze command-line flags
- X */
- Xboolean report_indirects = FALSE; /* command line option -- report
- X * files with indirect data blocks */
- Xboolean report_errors = FALSE; /* command line option -- report
- X * file size discrepancies */
- Xboolean debug = FALSE; /* report i-node #s as they are
- X * examined */
- Xboolean override = FALSE; /* override bad fs test */
- X
- X
- X/*
- X * interface to system error messages
- X */
- Xextern int errno;
- Xextern char *sys_errlist[];
- Xextern int sys_nerr;
- X
- X/*
- X * usage : prints out a short message describing command usage. This
- X * function does not return.
- X */
- Xvoid usage(){
- X printf ("File System analyzer - v2.01\n");
- X printf ("Usage:\n fsanalyze [-[deio]] special [file] ...\n");
- X printf ("\n\tIf the [file] argument(s) are missing, the entire\n");
- X printf ("\tfile system is scanned. Otherwise, only the specified\n");
- X printf ("\tfiles are examined. Valid flag are:\n\n");
- X printf ("\td\tdisplay i-node numbers as they are examined\n");
- X printf ("\te\treport file size inconsistencies\n");
- X printf ("\ti\treport data block double and triple indirection\n");
- X printf ("\to\toverride error checking on file system argument\n");
- X exit(1);
- X }
- X
- X/*
- X * check_fs : checks the file system to be sure it is not in need of
- X * checking. If the file system is damaged, the function displays a
- X * message from fsstat, then returns FALSE. Othersize, returns TRUE.
- X */
- Xboolean check_fs (special)
- Xchar *special;
- X{
- X char buffer[256]; /* buffer to hold cmd */
- X int fstat; /* exit status of fsstat */
- X
- X sprintf (buffer, "/etc/fsstat %s 2>/dev/null", special);
- X fstat = system (buffer);
- X if (fstat != 0 && !override){
- X if (fstat == 512 /* why is this byte-swapped? */){
- X fprintf (stderr, "warning: file system is mounted\n");
- X }
- X else {
- X /*
- X * run fsstat again to get error message (this is a kludge,
- X * but I hope more portable that having fsanalyze do the
- X * analysis itself)
- X */
- X sprintf (buffer, "/etc/fsstat %s", special);
- X system(buffer);
- X return (FALSE);
- X }
- X }
- X return (TRUE);
- X }
- X
- X/*
- X * error : prints out a formatted error string to stderr, followed if
- X * possible by an appropriate system error message. Control is then
- X * returned to the system with an error status. This function does not
- X * return.
- X */
- Xvoid error (err, str, arg1, arg2)
- Xint err; /* value of errno at time of call */
- Xchar *str; /* error string */
- Xchar *arg1, *arg2; /* additional printf arguments, if any */
- X{
- X fprintf (stderr, str, arg1, arg2);
- X if (err <= sys_nerr && err > 0)
- X fprintf (stderr, "%s\n", sys_errlist[err]);
- X else
- X fprintf (stderr, "unknown error : %d\n", err);
- X exit(1); /* exit with error status */
- X }
- X
- X/*
- X * init_stats : initializes per-file statistics structure
- X */
- Xvoid init_stats (data, inode)
- Xstruct file_data *data; /* file stats to init */
- Xint inode; /* i-node number */
- X{
- X data->inode = inode;
- X data->total_blocks = data->data_blocks = 0;
- X data->potential_seeks = data->seeks = 0;
- X data->fragm = 0.0;
- X }
- X
- X/*
- X * init : parses command line flags and the file system device name, then
- X * reads the file system super block. init returns the number of the
- X * parameter AFTER the file system device name. All subsequent parameters
- X * are assumed to be specific files to be tested.
- X */
- Xint init (argc, argv)
- Xint argc;
- Xchar *argv[];
- X{
- X int i; /* loop counter */
- X char *cp; /* cmd-line flag pointer */
- X boolean special_found = FALSE; /* TRUE = found special arg */
- X
- X for (i = 1; i < argc && !special_found; i++){
- X cp = argv[i];
- X if (*cp == '-'){
- X while (*++cp){
- X switch (*cp){
- X
- X /* report i-nodes with
- X * one or more levels of
- X * indirection */
- X case 'i':
- X report_indirects = TRUE;
- X break;
- X
- X /* report file size errors */
- X case 'e':
- X report_errors = TRUE;
- X break;
- X
- X /* debugging flag */
- X case 'd':
- X debug = TRUE;
- X break;
- X
- X /* override bad fs test */
- X case 'o':
- X override = TRUE;
- X break;
- X default:
- X fprintf (stderr, "illegal option : %c\n", *cp);
- X usage();
- X }
- X }
- X }
- X else {
- X special = argv[i];
- X special_found = TRUE;
- X }
- X }
- X if (special == NULL)usage(); /* no fs parameter */
- X
- X /*
- X * check for good file system (let fsstat do the dirty work!)
- X */
- X if (check_fs (special)){
- X
- X /*
- X * open file system and read the super block
- X */
- X if ((fsys=fopen (special, "r")) == NULL){
- X error (errno, "error opening \"%s\"\n", special);
- X /* NOTREACHED */
- X }
- X if (fseek (fsys, 512L, 0)){
- X error (errno, "error seeking superblock");
- X /* NOTREACHED */
- X }
- X if (fread (&super, sizeof (struct filsys), 1, fsys) != 1){
- X error (errno, "error reading superblock");
- X /* NOTREACHED */
- X }
- X
- X num_inodes = (super.s_isize-2) * 16;
- X if (super.s_magic != FsMAGIC)
- X fs_type = Fs1b;
- X else fs_type = super.s_type;
- X
- X return i; /* return # of next argument to be processed */
- X }
- X else {
- X exit(1); /* bad file system, error status */
- X }
- X }
- X
- X/*
- X * blk_no : given a pointer to a 3-byte binary number, returns a (long)
- X * block number. Used to access the first 10 data block numbers of an
- X * i-node. The function l3tol is used for portability.
- X */
- Xdaddr_t blk_no (off)
- Xunsigned char off[]; /* 3-byte offset */
- X{
- X extern void l3tol();
- X daddr_t temp = 0;
- X int n;
- X
- X l3tol (&temp, off, 1);
- X return temp;
- X }
- X
- X/*
- X * get_inodes : given an initial i-node number, reads a block of i-nodes
- X * into an i-node array.
- X */
- Xvoid get_inodes (in, inp, num)
- Xint in; /* inode number */
- Xint num; /* # inodes to get */
- Xstruct dinode *inp; /* buffer to hold info */
- X{
- X long position; /* computed position of the
- X * first requested i-node */
- X
- X position = (IBLK * BLK_SIZE * fs_type) +
- X (sizeof (struct dinode) * ((long)in-1));
- X
- X if (fseek (fsys, position, 0)){
- X error (errno, "\nerror seeking inode %d, pos = %ld\n", in, position);
- X /* NOTREACHED */
- X }
- X else {
- X if (fread (inp, sizeof (struct dinode), num, fsys) != num){
- X error (errno, "\nerror reading inode %d\n", in);
- X /* NOTREACHED */
- X }
- X }
- X }
- X
- X/*
- X * check_indirects : scans a block containing data block numbers, looking
- X * for block numbers that are not sequential.
- X */
- Xdaddr_t check_indirects (block, cur_pos, data)
- Xdaddr_t block; /* indirect block to check */
- Xdaddr_t cur_pos; /* current block offset */
- Xstruct file_data *data; /* current file statistics */
- X{
- X daddr_t indirect_blk[256]; /* holds an indirect block */
- X int num_blocks; /* number of data blocks in
- X * an indirect block */
- X daddr_t pos; /* current data block */
- X daddr_t new_pos; /* next data block */
- X int i; /* loop counter */
- X
- X num_blocks = 128 * fs_type;
- X data->total_blocks++;
- X
- X /*
- X * get the indirect block
- X */
- X if (fseek (fsys, block * 512L * fs_type, 0)){
- X error (errno, "\nerror seeking indirect block %ld\n", block);
- X /* NOTREACHED */
- X }
- X if (fread (indirect_blk, sizeof (daddr_t), num_blocks, fsys) != num_blocks){
- X error (errno, "\nerror reading indirect block %ld\n", block);
- X /* NOTREACHED */
- X }
- X pos = cur_pos;
- X
- X /*
- X * scan the data blocks looking for numbers out of sequence
- X */
- X for (i = 0; i < num_blocks; i++){
- X new_pos = indirect_blk [i];
- X if (new_pos == 0){
- X return pos;
- X }
- X data->data_blocks++;
- X data->total_blocks++;
- X data->potential_seeks++;
- X if (new_pos != pos + 1){
- X data->seeks++;
- X }
- X pos = new_pos;
- X }
- X return pos;
- X }
- X
- X/*
- X * check_double_indirects : scans a block containing a list of indirect
- X * blocks, checking for data block numbers that are out of sequence.
- X */
- Xdaddr_t check_double_indirects (block, cur_pos, data)
- Xdaddr_t block; /* indirect block to check */
- Xdaddr_t cur_pos; /* current block offset */
- Xstruct file_data *data; /* current file statistics */
- X{
- X daddr_t dindirect_blk[256]; /* holds a double-indirect
- X * block */
- X int i; /* loop counter */
- X int num_blocks; /* number of indirect blocks
- X * in a d-i block */
- X
- X num_blocks = 128 * fs_type;
- X data->total_blocks++;
- X
- X /*
- X * the double-indirect block itself should be in sequence with the
- X * data blocks
- X */
- X data->potential_seeks++;
- X if (block != cur_pos + 1){
- X data->seeks++;
- X }
- X
- X /*
- X * get the d-i block
- X */
- X if (fseek (fsys, block * 512L * fs_type, 0)){
- X error (errno, "\nerror seeking double indirect block %ld\n",
- X block);
- X /* NOTREACHED */
- X }
- X if (fread (dindirect_blk, sizeof (daddr_t), num_blocks, fsys) != num_blocks){
- X error (errno, "\nerror reading double indirect block %ld\n",
- X block);
- X /* NOTREACHED */
- X }
- X
- X /*
- X * scan through the d-i block
- X */
- X for (i = 0; i < num_blocks; i++){
- X if (dindirect_blk[i] == 0){
- X break;
- X }
- X cur_pos = check_indirects (dindirect_blk[i],
- X block,
- X data);
- X }
- X return cur_pos;
- X }
- X
- X/*
- X * check_triple_indirects : scans a block containing a list of double
- X * indirect blocks, looking for data block numbers that are out of sequence.
- X */
- Xdaddr_t check_triple_indirects (block, cur_pos, data)
- Xdaddr_t block; /* indirect block to check */
- Xdaddr_t cur_pos; /* current block offset */
- Xstruct file_data *data; /* current file statistics */
- X{
- X daddr_t tindirect_blk[256]; /* holds a triple-indirect
- X * block */
- X int i; /* loop counter */
- X int num_blocks; /* number of double-indirect
- X * blocks in a triple-i blk */
- X
- X
- X num_blocks = 128 * fs_type;
- X data->total_blocks++;
- X
- X /*
- X * the triple-indirect block itself should be in sequence with the
- X * data blocks
- X */
- X data->potential_seeks++;
- X if (block != cur_pos + 1){
- X data->seeks++;
- X }
- X
- X /*
- X * get the t-i block
- X */
- X if (fseek (fsys, block * 512L * fs_type, 0)){
- X error (errno, "\nerror seeking triple indirect block %ld\n",
- X block);
- X /* NOTREACHED */
- X }
- X if (fread (tindirect_blk, sizeof (daddr_t), num_blocks, fsys) != num_blocks){
- X error (errno, "\nerror reading triple indirect block %ld\n",
- X block);
- X /* NOTREACHED */
- X }
- X
- X /*
- X * scan through the t-i block
- X */
- X for (i = 0; i < num_blocks; i++){
- X if (tindirect_blk[i] == 0){
- X break;
- X }
- X cur_pos = check_double_indirects (tindirect_blk[i],
- X block,
- X data);
- X }
- X return cur_pos;
- X }
- X
- X/*
- X * check_file : scans the data block numbers of an i-node, looking for
- X * block numbers that are out of sequence, and would thus result in excess
- X * track-to-track seeking. This function also checks directory files for
- X * indirection (more than 10 data blocks), and performs a simple consistency
- X * check on all file sizes
- X */
- Xvoid check_file (inode, inode_number, data)
- Xstruct dinode *inode; /* inode info structure */
- Xint inode_number; /* inode number to be
- X * checked */
- Xstruct file_data *data; /* current file statistics */
- X{
- X daddr_t pos; /* current block */
- X daddr_t new_pos; /* next block in file */
- X int i; /* loop counter */
- X long file_size; /* file size computed by
- X * actual byte count */
- X
- X pos = blk_no (&inode->di_addr[0]); /* first data block */
- X
- X if (inode->di_size == 0 || pos == 0)return; /* ignore 0-size files */
- X
- X data->data_blocks = data->total_blocks = 1;
- X
- X /*
- X * do some simple-minded statistics gathering
- X */
- X if (FILE_TYPE (inode) == S_IFDIR){ /* got a directory */
- X num_directories++;
- X }
- X if (inode->di_nlink > 1){ /* multi-linked files */
- X linked_files++;
- X }
- X
- X /*
- X * scan the data blocks looking for numbers out of sequence
- X */
- X for (i = 1; i < 10; i++){
- X new_pos = blk_no (&inode->di_addr[i*3]);
- X if (new_pos == 0){ /* end of file */
- X break;
- X }
- X data->data_blocks++;
- X data->total_blocks++;
- X data->potential_seeks++;
- X if (new_pos != pos + 1){ /* out of sequence ? */
- X data->seeks++;
- X }
- X pos = new_pos;
- X }
- X
- X /*
- X * block 10, if non-zero, is the number of the block which contains
- X * the next (128 * fs_type) data block numbers. It should also be
- X * in sequence with the data blocks. Block 10 is called an
- X * "indirect" block.
- X */
- X if (blk_no (&inode->di_addr[10*3])){
- X indirects++;
- X
- X /*
- X * if a directory contains indirection, it is too large for
- X * efficient access. Report it.
- X */
- X if (FILE_TYPE (inode) == S_IFDIR){
- X printf ("inode %d is a large directory\n", inode_number);
- X big_directories++;
- X }
- X pos = check_indirects (blk_no (&inode->di_addr[10*3]),
- X pos,
- X data);
- X }
- X
- X /*
- X * block 11, if non-zero, is the number of the block which contains
- X * the next (128 * fs_type) INDIRECT block numbers. It should also be
- X * in sequence with the data blocks. Block 11 is called a "double-
- X * indirect" block.
- X */
- X if (blk_no (&inode->di_addr[11*3])){
- X double_indirects++;
- X if (report_indirects){
- X printf ("double indirection : %d\n", inode_number);
- X }
- X pos = check_double_indirects (blk_no (&inode->di_addr[11*3]),
- X pos,
- X data);
- X }
- X
- X /*
- X * block 12, if non-zero, is the number of the block which contains
- X * the next (128 * fs_type) DOUBLE-INDIRECT block numbers. It should
- X * also be in sequence with the data blocks. Block 12 is called a
- X * "triple-indirect" block.
- X */
- X if (blk_no (&inode->di_addr[12*3])){
- X triple_indirects++;
- X if (report_indirects){
- X printf ("triple indirection : %d\n", inode_number);
- X }
- X pos = check_triple_indirects (blk_no (&inode->di_addr[12*3]),
- X pos,
- X data);
- X }
- X
- X /*
- X * do a simple check to detect possible file-size errors (a la
- X * fsck phase 1)
- X */
- X file_size = (inode->di_size + (512 * fs_type - 1)) / (512 * fs_type);
- X if (file_size != data->data_blocks){
- X size_errors++;
- X if (report_errors){
- X printf ("inode %d, inconsistent file size : actual blocks = %ld, computed = %ld (%ld bytes)\n",
- X inode_number, data->data_blocks,
- X file_size, inode->di_size);
- X }
- X }
- X }
- X
- X/*
- X * log_stats : updates global statistics based on the current file statistics.
- X * The current file is then checked to see if it qualifies as one of the 10
- X * worst offenders (i.e., most fragmented) encountered thus far. The 10 worst
- X * offenders are determined based on their absolute number of disk seeks
- X * required to read the entire file. Such an absolute test (viz a viz a
- X * relative percentage test) ensures that very small, but fragmented, files
- X * will not clutter the output.
- X */
- Xvoid log_stats (data)
- Xstruct file_data *data; /* file statistics to be
- X * logged */
- X{
- X int i, j; /* loop counters */
- X
- X
- X /*
- X * update global statistics
- X */
- X blocks += data->total_blocks;
- X potential_seeks += data->potential_seeks;
- X seeks += data->seeks;
- X data->fragm = data->potential_seeks ? (float)data->seeks/(float)data->potential_seeks : 0.0;
- X
- X /*
- X * update 10 worst offender array
- X */
- X for (i = 0; i < 10; i++){
- X if (data->seeks > file_log[i].seeks){
- X for (j = 9; j > i; j--){
- X file_log[j] = file_log[j-1];
- X }
- X file_log[i] = *data;
- X break;
- X }
- X }
- X }
- X
- X/*
- X * scan : scan through each i-node of a file system, compiling statistics
- X * regarding fragmentation and indirection.
- X */
- Xvoid scan (){
- X int i, j; /* loop counters */
- X struct dinode i_node[I_BLOCK_FACTOR]; /* holds a block of inodes */
- X struct file_data data; /* per-inode statistics */
- X
- X for (i = 1; i < num_inodes; i+=I_BLOCK_FACTOR){
- X
- X /*
- X * for efficiency, read a block of i-nodes at a time
- X */
- X get_inodes (i, i_node, I_BLOCK_FACTOR);
- X
- X /*
- X * scan through each i-node that was read in
- X */
- X for (j = 0; i+j < num_inodes && j < I_BLOCK_FACTOR; j++){
- X if (debug){
- X printf ("inode %d\r", i+j);
- X }
- X if (i+j <= 1)continue; /* don't scan i-node 1 */
- X if (i_node[j].di_mode != 0){ /* unused i-node ? */
- X init_stats (&data, i+j);
- X check_file (&i_node[j], i+j, &data); /* scan blocks in file */
- X log_stats (&data);
- X }
- X else {
- X free_inodes++;
- X }
- X }
- X }
- X }
- X
- X/*
- X * print_report : calculates percentages and prints a summary report of
- X * file system statistics
- X */
- Xvoid print_report (){
- X long num_files; /* number of inodes used
- X * in file system */
- X int i; /* loop counter */
- X
- X float fragm, /* percent fragmentation */
- X ind_p, /* percent indirections */
- X dind_p, /* percent double indirects */
- X tind_p, /* percent triple indirects */
- X bused_p, /* percent data blocks used */
- X iused_p, /* percent inodes used */
- X bdir_p; /* percent of directories
- X * that have more than 10
- X * blocks */
- X
- X /*
- X * calculate percentages for report
- X */
- X fragm = potential_seeks ? (float)seeks/(float)potential_seeks : 0.0;
- X bused_p = (float)blocks/(float)(blocks+super.s_tfree);
- X num_files = num_inodes - free_inodes;
- X iused_p = (float)num_files/(float)num_inodes;
- X ind_p = num_files ? (float)indirects/(float)num_files : 0.0;
- X dind_p = num_files ? (float)double_indirects/(float)num_files : 0.0;
- X tind_p = num_files ? (float)triple_indirects/(float)num_files : 0.0;
- X bdir_p = num_directories ? (float)big_directories/(float)num_directories : 0.0;
- X
- X /*
- X * print out report
- X */
- X printf ("\n\nFile system name = \"%.6s\", Volume name = \"%.6s\"\n",
- X super.s_fname, super.s_fpack);
- X printf ("File system logical block size = %d bytes\n", 512 * fs_type);
- X printf ("Volume Size = %ld blocks (%ld bytes)\n",
- X super.s_fsize, super.s_fsize * 512L * fs_type);
- X printf ("\t%u blocks reserved for super block and inodes\n", super.s_isize);
- X printf ("\t%lu blocks reserved for data\n",
- X super.s_fsize - super.s_isize);
- X printf ("Total inodes = %d\n", num_inodes);
- X printf ("%.2f%% inodes used (%ld used, %ld free)\n",
- X iused_p*100, num_inodes - free_inodes, free_inodes);
- X printf ("%.2f%% data blocks used (%ld used, %ld free)\n",
- X bused_p*100, blocks, super.s_tfree);
- X printf ("%d directories\n", num_directories);
- X printf ("%d multiply-linked files\n", linked_files);
- X printf ("\nFragmentation = %.2f%%\n", fragm*100);
- X/* printf ("(%ld seeks of %ld potential)\n", seeks, potential_seeks); */
- X printf ("Indirects = %ld (%.2f%%)\n",
- X indirects, ind_p*100);
- X printf ("Double indirects = %ld (%.2f%%)\n",
- X double_indirects, dind_p*100);
- X printf ("Triple indirects = %ld (%.2f%%)\n",
- X triple_indirects, tind_p*100);
- X printf ("Oversized directories = %d (%.2f%%)\n",
- X big_directories, bdir_p*100);
- X printf ("10 worst offenders:\n");
- X printf (" i-node Size Fragments %%\ti-node Size Fragments %%\n");
- X for (i = 0; i < 5; i++){
- X if (file_log[i].inode != 0){
- X printf (" %6d %6ld %6ld %6.2f%%",
- X file_log[i].inode,
- X file_log[i].data_blocks,
- X file_log[i].seeks+1,
- X file_log[i].fragm*100);
- X if (file_log[i+5].inode != 0){
- X printf ("\t%6d %6ld %6ld %6.2f%%",
- X file_log[i+5].inode,
- X file_log[i+5].data_blocks,
- X file_log[i+5].seeks+1,
- X file_log[i+5].fragm*100);
- X }
- X printf ("\n");
- X }
- X else break;
- X }
- X }
- X
- X/*
- X * anal_file : analyzes a single file for fragmentation.
- X */
- Xvoid anal_file (ino, fname)
- Xint ino; /* i-node number */
- Xchar *fname; /* filename of this inode */
- X{
- X struct file_data data; /* current file statistics */
- X
- X struct dinode i_node; /* current inode data */
- X
- X get_inodes (ino, &i_node, 1);
- X init_stats(&data, ino);
- X check_file (&i_node, ino, &data);
- X log_stats (&data);
- X printf (" %-14s\t%6d %6ld %6ld %6.2f%%\n",
- X fname, data.inode, data.seeks+1,
- X data.total_blocks, data.fragm*100);
- X }
- X
- Xmain (argc, argv)
- Xint argc;
- Xchar *argv[];
- X{
- X int next_param;
- X struct stat f_stat;
- X
- X /*
- X * perform various initialization functions
- X */
- X next_param = init (argc, argv);
- X
- X if (next_param == argc){
- X /*
- X * no individual files to check, scan entire file system
- X */
- X printf ("Analyzing file system %s...\n", special);
- X
- X /*
- X * scan through all i-nodes in the file system
- X */
- X scan();
- X
- X /*
- X * print out statistics summary
- X */
- X print_report();
- X }
- X else {
- X /*
- X * scan individual files instead of entire file system
- X */
- X printf (" Name \ti-node Fragments Size %%\n");
- X for (; next_param < argc; next_param++){
- X if (stat (argv[next_param], &f_stat) != 0){
- X error (errno, "error opening \"%s\"\n",
- X argv[next_param]);
- X }
- X else {
- X if (!IS_SPECIAL (f_stat.st_mode)){
- X anal_file (f_stat.st_ino, argv[next_param]);
- X }
- X }
- X }
- X }
- X }
- END_OF_fsanalyze.c
- if test 25225 -ne `wc -c <fsanalyze.c`; then
- echo shar: \"fsanalyze.c\" unpacked with wrong size!
- fi
- # end of overwriting check
- fi
- echo shar: End of archive 1 \(of 1\).
- cp /dev/null ark1isdone
- MISSING=""
- for I in 1 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked all 1 archives.
- rm -f ark[1-9]isdone
- else
- echo You still need to unpack the following archives:
- echo " " ${MISSING}
- fi
- ## End of shell archive.
- exit 0
- --
- Mike Young - Software Development Technologies, Inc., Sudbury MA 01776
- UUCP : {decvax,harvard,linus,mit-eddie}!necntc!necis!mrst!sdti!mjy
- Internet : mjy%sdti.uucp@harvard.harvard.edu Tel: +1 617 443 5779
- "What would you do with a brain if you had one?" -- Dorothy, Wiz of Oz
-